package com.cellasoft.univrapp.widget; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.net.Uri; import android.util.AttributeSet; import android.view.View; import android.webkit.DownloadListener; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.ProgressBar; import com.cellasoft.univrapp.BuildConfig; import com.cellasoft.univrapp.utils.UIUtils; import java.lang.ref.WeakReference; import java.lang.reflect.Field; import static com.cellasoft.univrapp.utils.LogUtils.LOGD; /** * see http://stackoverflow.com/questions/3130654/memory-leak-in-webview and * http://code.google.com/p/android/issues/detail?id=9375 Note that the bug does * NOT appear to be fixed in android 2.2 as romain claims * <p/> * Also, you must call {@link #destroy()} from your activity's onDestroy method. */ public class NonLeakingWebView extends WebView { private static final String ASSETS_PATH = "file:///android_asset/"; private static Field sConfigCallback; transient private static ProgressBar web_progressBar; static { try { sConfigCallback = Class.forName("android.webkit.BrowserFrame") .getDeclaredField("sConfigCallback"); sConfigCallback.setAccessible(true); } catch (Exception e) { // ignored } } public NonLeakingWebView(Context context) { super(context.getApplicationContext()); init(context); } public NonLeakingWebView(final Context context, final AttributeSet attrs) { super(context.getApplicationContext(), attrs); init(context); } public NonLeakingWebView(final Context context, final AttributeSet attrs, final int defStyle) { super(context.getApplicationContext(), attrs, defStyle); init(context); } private void init(final Context context) { setWebViewClient(new MyWebViewClient((Activity) context)); getSettings().setAllowFileAccess(true); getSettings().setAppCacheEnabled(true); getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY); setDownloadListener(new DownloadListener() { @Override public void onDownloadStart(final String url, final String userAgent, final String contentDisposition, final String mimetype, final long contentLength) { UIUtils.safeOpenLink(context, new Intent(Intent.ACTION_VIEW, Uri.parse(url)).setType(mimetype)); } }); } public void showArticle(final String html) throws Exception { if (html != null && html.length() > 0) { loadDataWithBaseURL(ASSETS_PATH, html, "text/html", "UTF-8", null); // scroll the webview up to the top scrollTo(0, 0); } } @Override public void destroy() { super.destroy(); if (sConfigCallback != null) { try { sConfigCallback.set(null, null); } catch (Exception e) { // throw new RuntimeException(e); } } } public void setProgressBar(ProgressBar pbar) { web_progressBar = pbar; } protected static class MyWebViewClient extends WebViewClient { transient protected WeakReference<Activity> activityRef; public MyWebViewClient(Activity activity) { super(); this.activityRef = new WeakReference<Activity>(activity); } @Override public boolean shouldOverrideUrlLoading(final WebView view, final String url) { try { final Activity activity = activityRef.get(); if (activity != null) { UIUtils.safeOpenLink(activity, new Intent( Intent.ACTION_VIEW, Uri.parse(url))); } } catch (RuntimeException ignored) { // ignore any url parsing exceptions } return true; } @Override public void onPageStarted(final WebView view, final String url, final Bitmap favicon) { if (BuildConfig.DEBUG) { LOGD("WebView", "onPageStarted"); } super.onPageStarted(view, url, favicon); if (web_progressBar != null) { web_progressBar.setVisibility(View.VISIBLE); } } @Override public void onPageFinished(final WebView view, final String url) { if (BuildConfig.DEBUG) { LOGD("WebView", "onPageFinished "); } super.onPageFinished(view, url); if (web_progressBar != null) { web_progressBar.setVisibility(View.GONE); } } } }